// (C) Copyright 2012 Enlightv. All rights reserved.

/*
    O_result = I_numer / I_denum
*/

`timescale 1ns/100ps

module serial_divider
#(parameter
    INT_BW = 8,
    FRAC_BW = 8
)
(
    input  I_sclk,
    input  I_rst_n,
    input  [ INT_BW - 1: 0] I_numer,
    input  [ INT_BW - 1: 0] I_denum,
    input  I_divide_start,
    output reg [ INT_BW + FRAC_BW - 1: 0] O_result,
    output reg O_divide_finish
);

/******************************************************************************
                                <localparams>
******************************************************************************/

/******************************************************************************
                              <internal signals>
******************************************************************************/
reg  [ INT_BW + FRAC_BW + 1: 0] numer_init;
reg  [ INT_BW : 0] numer_comp;
reg  [ INT_BW : 0] denum_comp;
wire finish;
wire shift;
wire [ INT_BW - 1: 0] comp_result;
wire quo_result;
reg  [ INT_BW + FRAC_BW - 1: 0] quotient;

/******************************************************************************
                                <module body>
******************************************************************************/
always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        numer_init <= 'd0;
    else if (I_divide_start)
        numer_init <= {I_numer,{(FRAC_BW+1){1'b0}},1'b1};
    else
        numer_init <= numer_init << 1;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        denum_comp <= 'd0;
    else if (I_divide_start)
        denum_comp <= I_denum;

assign finish = numer_init[INT_BW+FRAC_BW+1] & ~|numer_init[INT_BW+FRAC_BW:0];
assign comp_result = shift ? numer_comp[INT_BW-1:0] : numer_comp + ~denum_comp + 1'b1;
assign quo_result = !shift;

comparator #( .BW(INT_BW+1)) u_coparator
(
    .I_a(numer_comp),
    .I_b(denum_comp),
    .O_greater(),
    .O_greater_or_equal(),
    .O_less(shift),
    .O_less_or_equal()
);

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        begin
        numer_comp <= 'd0;
        quotient <= 'd0;
        end
    else if (I_divide_start || finish)
        begin
        numer_comp <= 'd0;
        quotient <= 'd0;
        end
    else
        begin
        numer_comp <= {comp_result,numer_init[INT_BW+FRAC_BW+1]};
        quotient <= {quotient[INT_BW+FRAC_BW-2:0],quo_result};
        end

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_divide_finish <= 1'b0;
    else
        O_divide_finish <= finish;

always @(posedge I_sclk or negedge I_rst_n)
    if (!I_rst_n)
        O_result <= 'd0;
    else if (finish)
        O_result <= quotient;

endmodule

